//  SMPSO.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package hidra.many.metaheuristics.approaches.CSMOPSOv2;

import hidra.core.population.HIDRAPopulationAlgorithm;
import hidra.jmetal.core.Operator;
import hidra.jmetal.core.Problem;
import hidra.jmetal.core.Solution;
import hidra.jmetal.core.SolutionSet;
import hidra.qualityIndicator.Hypervolume;
import hidra.qualityIndicator.QualityIndicator;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Vector;
import java.util.logging.FileHandler;
import java.util.logging.Logger;

import jmetal.operators.mutation.Mutation;
import jmetal.operators.mutation.MutationFactory;
import jmetal.util.Configuration;
import jmetal.util.Distance;
import jmetal.util.JMException;
import jmetal.util.PseudoRandom;
import jmetal.util.archive.CrowdingArchive;
import jmetal.util.comparators.CrowdingDistanceComparator;
import jmetal.util.comparators.DominanceComparator;
import jmetal.util.comparators.ObjectiveComparator;
import jmetal.util.wrapper.XReal;

/**
 * This class implements the SMPSO algorithm described in:
 * A.J. Nebro, J.J. Durillo, J. Garcia-Nieto, C.A. Coello Coello, F. Luna and E. Alba
 * "SMPSO: A New PSO-based Metaheuristic for Multi-objective Optimization". 
 * IEEE Symposium on Computational Intelligence in Multicriteria Decision-Making 
 * (MCDM 2009), pp: 66-73. March 2009
 */
public class MAHYSMPSO extends HIDRAPopulationAlgorithm {

  
  private int archiveSize_;  
  private SolutionSet leaders_;


  QualityIndicator indicators_; // QualityIndicator object

  private double trueHypervolume_;
  private Hypervolume hy_;
  private SolutionSet trueFront_;
 
  private static int NPAS_SUB = 30;
  private static int ARH_SIZE = 50;
  /** 
   * Constructor
   * @param problem Problem to solve
   */
  public MAHYSMPSO(Problem problem) {
    super(problem) ;    
  } // Constructor

    

  public MAHYSMPSO(Problem problem, String trueParetoFront) throws FileNotFoundException {
    super(problem) ;
    hy_ = new Hypervolume();
    hidra.qualityIndicator.util.MetricsUtil mu = new hidra.qualityIndicator.util.MetricsUtil();
    trueFront_ = mu.readNonDominatedSolutionSet(trueParetoFront);
    trueHypervolume_ = hy_.hypervolume(trueFront_.writeObjectivesToMatrix(),
      trueFront_.writeObjectivesToMatrix(),
      problem_.getNumberOfObjectives());

  } // Constructor

  /**
   * Initialize all parameter of the algorithm
   */
  
  ArrayList<SUBSMPSO> subswarms;
  
  public void initParams() {   
	maxIterations_ = ((Integer) getInputParameter("maxIterations")).intValue();
    indicators_ = (QualityIndicator) getInputParameter("indicators");
    subswarms = new ArrayList<SUBSMPSO>();
    iteration_ = 0 ;
    leaders_ = new CrowdingArchive(archiveSize_, problem_.getNumberOfObjectives());
  } // initParams 

 
  
   @Override
  protected SolutionSet getParetoFront() {
  	// TODO Auto-generated method stub
  	return this.leaders_;
  }

  
  
  
  
  @Override
  protected SolutionSet initializationAlgorithm() throws ClassNotFoundException,
  		JMException {
  	
	    initParams();

	    int n = problem_.getNumberOfObjectives();
	    
	    
	    
	    for(int i=0; i < n; i++){
	    	SUBSMPSO algorithm = new SUBSMPSO(problem_);	    	
	    	algorithm.setInputParameter("swarmSize",NPAS_SUB);
	        algorithm.setInputParameter("archiveSize",ARH_SIZE);
	        
	        HashMap parameters = new HashMap() ;
	        parameters.put("probability", 1.0/problem_.getNumberOfVariables()) ;
	        parameters.put("distributionIndex", 20.0) ;
	        Mutation mutation = MutationFactory.getMutationOperator("PolynomialMutation", parameters);                    

	        algorithm.addOperator("mutation", mutation);	    		    	
	    	algorithm.setIndex(i);
	    	
	    	algorithm.initializationAlgorithm();
	    	
	    	subswarms.add(algorithm);
	    }
	    
		return this.leaders_;	  	 
		
  }

  private int epoch = 10;
  
  @Override
  protected SolutionSet runIteration() throws JMException {  		
	  //Parallel execution
	  
	  for(int i=0;  i < subswarms.size() ;i++){
		  subswarms.get(i).runIteration();
	  }
	  
	  iteration_++;
	  
	
	  HashMap<Integer, SolutionSet> mapLeaders
	  							= new HashMap<Integer, SolutionSet>();
	  
	  //migration
	  if((iteration_ % epoch) == 0){
		  
		  for(int i=0;  i < subswarms.size() ;i++){
			  SolutionSet leaders = subswarms.get(i).getLeader();
			  mapLeaders.put(i, leaders);
		  }  
		  		  		  
		  for(int i=0;  i < subswarms.size() ;i++){
			  SolutionSet leaders = mapLeaders.get(i);
			  
			  for(int j=0;  j < subswarms.size() ;j++){
				  
				  if(i != j){
					  
					subswarms.get(j).insertInExternArchive(leaders);  
					  
				  }
				  
				  
			  }
			  
		  }
		  
	  }
	  
	  
	  SolutionSet solutionSet = getExtremSolutions();
	  
	  leaders_ = solutionSet;
	  
	  return solutionSet;
	  
	  
  }


  
  
  
  
  private SolutionSet getExtremSolutions(){	 
	  
	  SolutionSet solutionSet = new SolutionSet(problem_.getNumberOfObjectives());
	  
	  
	  for(int i=0;  i < subswarms.size() ;i++){
		  
		  SolutionSet leaders = subswarms.get(i).getLeader(); 
		  leaders.sort(new ObjectiveComparator(1));
		  
		  int thirdQuat  = (int)(0.75 * leaders.size());
		  int firstQuat = (int)(0.25 * leaders.size());
			
		   double range = 
				leaders.get(thirdQuat).getObjective(1)
								- leaders.get(firstQuat).getObjective(1);
		  
		   range = 1.5 * range;
		   double limit = leaders.get(thirdQuat).getObjective(1);
		   limit = limit + range;
		   double norm = 0.0;
			
		  
		   for(int j = thirdQuat+1;  j < leaders.size()  ; ){
				
				norm = leaders.get(j).getObjective(1);
				
				if( norm > limit){
					leaders.remove(j);	
				}else{
					j++;
				}
				
				
		   }
		   		   
		   leaders.sort(new ObjectiveComparator(0));		  
		   Solution solution = leaders.get(0);		  
		   solutionSet.add(solution);		   		   
	  }
	  	    
	  return solutionSet;	  
	  
  }
  
  
  
  /** 
   * Gets the leaders of the SMPSO algorithm
   */
  public SolutionSet getLeader() {
    return leaders_;
  }  // getLeader   

  
  public void print() throws IOException{
	  
	  	Logger   logger_ ;      // Logger object
	  	FileHandler fileHandler_;
	  
	  // Logger object and file to store log messages
	    logger_      = Configuration.logger_ ;
	    fileHandler_ = new FileHandler("SMPSO_main.log"); 
	    logger_.addHandler(fileHandler_) ;
	  
	  
	// Result messages 
	    
	    logger_.info("Objectives values have been writen to file FUN");
	    leaders_.printObjectivesToFile("FUN");
	    logger_.info("Variables values have been writen to file VAR");
	    leaders_.printVariablesToFile("VAR");      
	    
	    
	    
	    
	   SolutionSet newSolutionSet =  transform(leaders_);
	    
	   newSolutionSet.printObjectivesToFile("FUN2");
	  
  }


  
  public SolutionSet transform(SolutionSet solutionSet){	
		SolutionSet newSolutionSet = new SolutionSet(solutionSet.size()); 

		for(int i=0; i < solutionSet.size() ;i++){		
			Solution solution =  solutionSet.get(i);		
			Solution newSolution;
			try {
				newSolution = new Solution(this.problem_);
				
				for(int j=0; j < this.problem_.getNumberOfVariables() ;j++){
					double temp = solution.getDecisionVariables()[j].getValue();
					newSolution.getDecisionVariables()[j].setValue(temp);
				}
																
				problem_.evaluate(newSolution);			
				newSolutionSet.add(newSolution);
					
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (JMException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
	   }
		
		
		return newSolutionSet;
		
	}
  

} // SMPSO
